package org.duang.db.elasticsearch;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.duang.db.IdEntity;
import org.duang.db.common.Page;
import org.duang.db.common.Query;
import org.duang.db.mongo.parser.DecodeConvetor;
import org.duang.kit.ClassKit;
import org.duang.kit.ToolsKit;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.client.Client;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;

/**
 * 基于ElasticSearch搜索，目前仅支持字段匹配查询(相当于模糊查询)
 * 
 * @author laotang
 * @param <T>
 */
public class ElasticSearchBaseDao<T> {

	private final static Logger logger = LoggerFactory.getLogger(ElasticSearchBaseDao.class);
	protected Class<T> cls;
	protected Client client;
	protected String database;
	protected String coll;

	public ElasticSearchBaseDao() {

	}

	public ElasticSearchBaseDao(Class<T> cls) {
		boolean isExtends = ClassKit.isExtends(cls, IdEntity.class.getCanonicalName());
		if (!isExtends) {
			throw new RuntimeException("the " + cls.getCanonicalName() + " is not extends "
					+ IdEntity.class.getCanonicalName() + ", exit...");
		}
		client = ESearch.getInstance().getESClient();
		database = ESearch.getInstance().getElasticSearchIndexName();
		coll = ClassKit.getBeanName(cls);
		this.cls = cls;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private SearchRequestBuilder buildESQuery(Query query) {
		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
		Map<String, Object> map = query.getQueryObj().toMap();
		for (Iterator<Entry<String, Object>> it = map.entrySet().iterator(); it.hasNext();) {
			Entry<String, Object> entry = it.next();
			Object value = entry.getValue();
			if (value instanceof DBObject) {
				continue;
			}
			queryBuilder.must(QueryBuilders.matchQuery(entry.getKey(), (String) value));
			// queryBuilder.must(QueryBuilders.queryStringQuery((String)value).field(entry.getKey()));
		}
		DBObject order = query.getDBOrder();
		FieldSortBuilder sortBuilder = null;
		for (Iterator<String> it = order.keySet().iterator(); it.hasNext();) {
			String key = it.next();
			SortOrder value = ((Integer) order.get(key)).intValue() == 1 ? SortOrder.ASC : SortOrder.DESC;
			sortBuilder = SortBuilders.fieldSort(key).order(value);
		}
		SearchRequestBuilder builder = client.prepareSearch(database).setTypes(coll);
		if (ToolsKit.isNotEmpty(sortBuilder)) {
			builder.addSort(sortBuilder);
		}
		if (ToolsKit.isNotEmpty(queryBuilder)) {
			builder.setQuery(queryBuilder);
		}
		Page page = query.getPage();
		if (ToolsKit.isNotEmpty(page)) {
			builder.setFrom(page.getPageNo()).setSize(page.getPageSize());
		}
		logger.debug(builder.toString());
		return builder;
	}

	private SearchHits getHits(Query query) {
		return buildESQuery(query).execute().actionGet().getHits();
	}

	public T findOne(Query query) throws Exception {
		SearchHits hits = getHits(query);
		DBObject resultDbo = new BasicDBObject();
		if (ToolsKit.isNotEmpty(hits.getHits())) {
			resultDbo.putAll(hits.getHits()[0].getSource());
		}
		return DecodeConvetor.convetor(cls, resultDbo);
	}

	public List<T> findList(Query query) throws Exception {
		SearchHits hits = getHits(query);
		if (ToolsKit.isEmpty(hits))
			return null;
		List<T> dbs = new ArrayList<T>(hits.getHits().length);
		for (Iterator<SearchHit> it = hits.iterator(); it.hasNext();) {
			SearchHit hit = it.next();
			DBObject resultDbo = new BasicDBObject();
			resultDbo.putAll(hit.getSource());
			dbs.add(DecodeConvetor.convetor(cls, resultDbo));
		}
		return dbs;
	}

	@SuppressWarnings("unchecked")
	public Page<T> findPage(Query query) throws Exception {
		SearchHits hits = getHits(query);
		if (ToolsKit.isEmpty(hits))
			return null;
		Page<T> page = query.getPage();
		page.setResult(findList(query));
		if (page.isAutoCount()) {
			page.setTotalCount(hits.getTotalHits());
		}
		return page;
	}

	/*
	 * public T findOne(Query query) throws Exception { // Map<String, Object>
	 * map = new HashMap<String, Object>(); // map.put("foodname", "椰"); //
	 * map.put("typename", "蛋"); // Map<String, Map<String, Object>> totalMap =
	 * new HashMap<String, Map<String, Object>>(); // totalMap.put("term", map);
	 * // System.out.println(ToolsKit.toJsonString(QueryBuilders.termQuery(
	 * "foodname", "椰").toString())); //
	 * System.out.println(ToolsKit.toJsonString(map)); // TermQueryBuilder term
	 * = QueryBuilders.termQuery("foodname", "椰"); // TermQueryBuilder term2 =
	 * QueryBuilders.termQuery("typename", "蛋"); // Map<String,
	 * TermQueryBuilder> map = new HashMap<String, TermQueryBuilder>(); //
	 * QueryBuilder ss = QueryBuilders.termQuery("typename", "蛋"); // // // //
	 * SearchRequestBuilder builder = client.prepareSearch("syt") //
	 * .setTypes(cls.getSimpleName()); ////
	 * .setQuery(ToolsKit.toJsonString(totalMap)); //
	 * builder.setQuery(QueryBuilders.termQuery("foodname", "椰")); // // //
	 * SearchRequestBuilder builder2 = client.prepareSearch("syt") //
	 * .setTypes(cls.getSimpleName()); ////
	 * .setQuery(ToolsKit.toJsonString(totalMap)); //
	 * builder2.setFrom(0).setSize(20).setQuery(QueryBuilders.termQuery(
	 * "typename", "蛋")); // ////// ////// builder.setExtraSource(map); //
	 * System.out.println(builder.toString()); // SearchHits hits =
	 * builder.execute() // .actionGet() // .getHits(); //// SearchHits hits =
	 * client.prepareSearch("syt") //// .setTypes(cls.getSimpleName()) ////
	 * .setQuery(QueryBuilders.matchQuery("foodname", "椰")) ////
	 * .setQuery(QueryBuilders.matchQuery("typename", "蛋1")) //////
	 * .setQuery(builder.toString()) //// .execute() //// .actionGet() ////
	 * .getHits(); // if(ToolsKit.isEmpty(hits)) return null; // DBObject
	 * resultDbo = new BasicDBObject(); // MultiSearchRequestBuilder mBuilder =
	 * client.prepareMultiSearch().add(builder).add(builder2); //
	 * System.out.println(ToolsKit.toJsonString(mBuilder.request().requests()));
	 * // Item[] items = mBuilder.execute().actionGet().getResponses(); //
	 * System.out.println("items.length: " + items.length); // for(Item item :
	 * items){ // hits = item.getResponse().getHits(); // System.out.println(
	 * "total: " + hits.getTotalHits()); // for(Iterator<SearchHit> it =
	 * hits.iterator(); it.hasNext();) { // SearchHit hit = it.next(); //
	 * System.out.println(ToolsKit.toJsonString(hit)); // } // } // QueryBuilder
	 * b1= QueryBuilders.termQuery("foodname", "椰"); RangeQueryBuilder rq =
	 * QueryBuilders.rangeQuery("protein").from(5).to(10);
	 * 
	 * // QueryBuilder b2 =
	 * QueryBuilders.spanOrQuery().clause(QueryBuilders.spanTermQuery(
	 * "typename", "蛋")); QueryBuilder b2 =
	 * QueryBuilders.queryStringQuery("typename=蛋"); QueryBuilder queryBuilder =
	 * QueryBuilders.boolQuery().must(b1).must(rq).should(b2);
	 * System.out.println(queryBuilder.toString()); SearchHits hits =
	 * client.prepareSearch("syt").setTypes(cls.getSimpleName()).setQuery(
	 * queryBuilder).setSize(20).execute().actionGet().getHits();
	 * for(Iterator<SearchHit> it = hits.iterator(); it.hasNext();) { SearchHit
	 * hit = it.next(); System.out.println(ToolsKit.toJsonString(hit)); } //
	 * resultDbo.putAll(hit.getSource());
	 * 
	 * 
	 * // return null;//DecodeConvetor.convetor(cls, resultDbo); }
	 */
}
